package main

import (
	"google.golang.org/protobuf/compiler/protogen"
	"os"
	"path"
)

func supplement(plugin *protogen.Plugin, file *protogen.File) {
	if len(file.Services) == 0 {
		return
	}
	generatedFile := plugin.NewGeneratedFile(
		file.GeneratedFilenamePrefix+supplementFilenameExtension,
		file.GoImportPath,
	)
	// 确保目标目录存在
	if err := os.MkdirAll(path.Dir(file.GeneratedFilenamePrefix), 0755); err != nil {
		plugin.Error(err)
		return
	}
	//常量、接口、类型定义
	supplementConstants(generatedFile, file)
	//注册函数
	supplementRegister(generatedFile, file)
	//填充路由
	supplementFillRouter(generatedFile, file)

	//全局函数
	supplementVarFunc(generatedFile, file)

	for _, service := range file.Services {
		//grpc方法转connect方法
		supplementGrpcToConnectHandler(generatedFile, service)
	}
}

// grpc方法转connect方法
func supplementGrpcToConnectHandler(g *protogen.GeneratedFile, service *protogen.Service) {
	name := changeServiceName(service)
	for _, method := range service.Methods {
		g.P("func (s *", name.UnimplementedServiceName, ") ", method.GoName,
			"(ctx ", g.QualifiedGoIdent(contextPackage.Ident("Context")), ", req *", connectPackage.Ident("Request"), "[", method.Input.GoIdent, "]) (*",
			connectPackage.Ident("Response"), "[", method.Output.GoIdent, "],error) {")
		if method.Desc.IsStreamingServer() {
			g.P("return ", connectPackage.Ident("NewError"), "(",
				connectPackage.Ident("CodeUnimplemented"), ", ", errorsPackage.Ident("New"),
				`("`, method.Desc.FullName(), ` is not implemented"))`)
		} else {
			g.P("r,err := s.srv.", method.Desc.Name(), "(ctx,req.Msg)")
			g.P("if err != nil {")
			g.P("return nil,err}")
			g.P("return &", g.QualifiedGoIdent(connectPackage.Ident("Response"))+"["+
				g.QualifiedGoIdent(method.Output.GoIdent)+"]{")
			g.P("Msg: r,")
			g.P("},nil")
		}
		g.P("}")
		g.P()
	}
	g.P()

}

// 全局函数
func supplementVarFunc(g *protogen.GeneratedFile, file *protogen.File) {
	g.P()
	if !isMethodGenerated("ContainString") {
		g.P("func ContainString(arr []string,str string) bool {")
		g.P("for _,v := range arr {")
		g.P("if v == str {")
		g.P("return true")
		g.P("}")
		g.P("}")
		g.P("return false")
		g.P("}")
		g.P()
		markMethodGenerated("ContainString")
	}

	if !isMethodGenerated("errorFunc") {
		g.P("func errorFunc(ret, data any) []byte {")
		g.P("da := map[string]interface{}{")
		g.P(`"ret":  ret,`)
		g.P(`"data": data,`)
		g.P("}")
		g.P("daString, _ := ", jsonPackage.Ident("Marshal"), "(da)")
		g.P("return daString")
		g.P("}")
		g.P()
		markMethodGenerated("errorFunc")
	}

	if !isMethodGenerated(Global.StartName) {
		g.P("func ", Global.StartName, "() *", Global.HandlerNameType, "{")
		for _, service := range file.Services {
			name := changeServiceName(service)
			g.P(name.ServiceDescriptor, ` = `,
				g.QualifiedGoIdent(file.GoDescriptorIdent),
				`.Services().ByName("`, service.Desc.Name(), `")`)
		}
		g.P("allRouter = make( ", Global.HandlerNameType, " , 1)")
		g.P("return &", Global.AllRouterName)
		g.P("}")
		g.P()
		markMethodGenerated(Global.StartName)
	}

	if !isMethodGenerated(Global.HandlerFilter) {
		g.P("func (s *", Global.HandlerNameType, ")", Global.HandlerFilter, "(filter ...", Global.HttpFilter, ") *", Global.HandlerNameType, "{")
		g.P("for _,httpFilter := range filter {")
		g.P("(*s)[httpFilter.ServerName] = ", Global.HandlerConf, " {")
		g.P("Filter:httpFilter,")
		g.P("}")
		g.P("}")
		g.P("return s")
		g.P("}")
		g.P()
		markMethodGenerated(Global.HandlerFilter)
	}

}

// 全局变量定义
func supplementConstants(g *protogen.GeneratedFile, file *protogen.File) {
	g.P("package ", file.GoPackageName)
	g.P()
}

func supplementFillRouter(g *protogen.GeneratedFile, file *protogen.File) {
	if isMethodGenerated(Global.FillRouterName) {
		return
	}
	g.P()
	g.P("func ", Global.FillRouterName, "(field ", reflectPackage.Ident("Value"), ", opts ...", connectPackage.Ident("HandlerOption"), ") {")
	g.P("switch sr := field.Addr().Interface().(type) {")
	for _, service := range file.Services {
		name := changeServiceName(service)
		g.P("case ", name.GrpcHttpHandler, ":")
		g.P("conf := ", Global.AllRouterName, "[", procedureServiceKeyPath(service), "]")
		g.P("conf.HttpServer = ", httpPackage.Ident("HandlerFunc"), `(func(w `, httpPackage.Ident("ResponseWriter"), `, r *`, httpPackage.Ident("Request"), ") {")

		g.P("lastSegment := ", pathPackage.Ident("Base"), "(r.URL.Path)")
		g.P("if conf.Filter.Func != nil && !ContainString(conf.Filter.WhiteList,lastSegment) {")
		g.P("err := conf.Filter.Func(w,r)")
		g.P("if err != nil {")
		g.P("w.Write(errorFunc(", httpPackage.Ident("StatusUnauthorized"), ", err.Error()))")
		g.P("return")
		g.P("}")
		g.P("}")

		g.P("if value,ok := ", name.ServerProxyName, "[r.URL.Path]; ok {")
		g.P("value(&", name.UnimplementedServiceName, "{srv: sr}, opts...).ServeHTTP(w, r)")
		g.P("} else {")
		g.P(httpPackage.Ident("NotFound"), "(w, r)")
		g.P("}")
		g.P("})")
		g.P(Global.AllRouterName, "[", procedureServiceKeyPath(service), "] = conf")
	}
	g.P("}")
	g.P("}")
	g.P()
	markMethodGenerated(Global.FillRouterName)
}
